#include <stdlib.h>
#if defined(__APPLE__) || defined(MACOSX)
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif
#include <stdio.h>
#include <math.h>
#include "luces.h"
#include "modelado.h"
#include "revolucion.h"
#include "matematicas.h"
#include "material.h"
#include "texturas.h"

/**
 * Handler del ratón
 */
void botonRatonPulsado(int boton, int estado, int x, int y) {
	if (boton == GLUT_LEFT_BUTTON) {
		if (estado == GLUT_DOWN) {
			botonIzquierdoPulsado = BOTON_PULSADO;
			pInicio = pFin = actualizaPunto3D(x - (w / 2), (h / 2) - y, 0.0f);
		} else if (estado == GLUT_UP) {
			botonIzquierdoPulsado = BOTON_NO_PULSADO;
			asigna(matrizActual, producto);
		}
	} else if (boton == GLUT_RIGHT_BUTTON) {
		if (estado == GLUT_DOWN) {
			botonDerechoPulsado = BOTON_PULSADO;
			pInicio = actualizaPunto3D(x, y, 0.0f);
		} else if (estado == GLUT_UP) {
			botonDerechoPulsado = BOTON_NO_PULSADO;
			asigna(matrizActual, producto);
		}
	} else if (boton == GLUT_MIDDLE_BUTTON) {
		if (estado == GLUT_DOWN) {
			botonMedioPulsado = BOTON_PULSADO;
			pInicio = actualizaPunto3D(x, y, 0.0f);
		} else if (estado == GLUT_UP) {
			botonMedioPulsado = BOTON_NO_PULSADO;
			asigna(matrizActual, producto);
		}
	}
}

/**
 * Comprobar dónde está el ratón
 */
int dentroVentana(int x, int y) {
	int dentro = DENTRO;

	if (x < 0 || y < 0 || x > w || y > h)
		dentro = FUERA;

	return (dentro);
}

/**
 * Handler movimiento del ratón
 */
void ratonMovido(int x, int y) {
	if (botonIzquierdoPulsado == BOTON_PULSADO && dentroVentana(x, y)) {
		pFin = actualizaPunto3D(x - (w / 2), (h / 2) - y, 0.0f);
		if (distintos(pInicio, pFin) == DISTINTOS) {
			calculaMatrizModeloVista(pFin, pInicio, matriz, R2);
			multiplicaMatrices(matriz, matrizActual, producto);
			glutPostRedisplay();
		}
	} else if (botonDerechoPulsado == BOTON_PULSADO && dentroVentana(x, y)) {
		pFin = actualizaPunto3D(x, y, 0.0f);
		dx += (float) (pFin.x - pInicio.x) / 200.0f;
		dy += (float) (pInicio.y - pFin.y) / 200.0f;
		pInicio = pFin;
		glutPostRedisplay();
	} else if (botonMedioPulsado == BOTON_PULSADO && dentroVentana(x, y)) {
		pFin = actualizaPunto3D(x, y, 0.0f);
		dz -= (float) (pInicio.y - pFin.y) / 200.0f;
		pInicio = pFin;
		glutPostRedisplay();
	}
}

/**
 * Detector de tecla ESC
 */
void Key(unsigned char tecla, int x, int y) {
	switch (tecla) {
	case 27:
		exit(0);
		break;
	}
}

/**
 * Borra la ventana y establece el color de dibujo y el ancho de las lineas
 */
void display(void) {
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
	// glClear (GL_COLOR_BUFFER_BIT);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Borrar Buffer de profundidad

	glLoadIdentity();

	glPushMatrix();
	glTranslatef(dx, dy, dz);
	glMultMatrixf(producto);

	// 1ª pasada. Dibujar polígonos:
	/*
	 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	 glEnable(GL_POLYGON_OFFSET_FILL);
	 glPolygonOffset(1.0, 1.0);
	 glColor3f(0.0f, 0.0f, 0.0f);
	 */
	glCallList(escenario);

	// 2ª pasada. Dibujar aristas:
	/* 
	 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
	 glDisable(GL_POLYGON_OFFSET_FILL);
	 glColor3f(0.25f, 0.75f, 1.0f);
	 glCallList(escenario);
	 */

	glPopMatrix();
	glutSwapBuffers();
}

/**
 * Asocia las funciones a los eventos del teclado
 */
void inicializaTeclado(void) {
	glutKeyboardFunc(Key);
}

/**
 * Especifica las caracteristicas de la vista...
 */
void inicializaVista(void) {
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	dz = -5.0f;

//	glOrtho(-2.5f, 2.5f, -2.5f, 2.5f, -4.0f, 4.0f);
//	gluPerspective(fovy, aspecto, cerca, lejos);

	gluPerspective(45.0f, (GLdouble) w / h, 0.1, 20.0);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

//	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	glFrontFace(GL_CCW);
	glCullFace(GL_BACK);
	glEnable(GL_NORMALIZE); // Habilita las normales
	glEnable(GL_CULL_FACE);
	glEnable(GL_DEPTH_TEST); // Activar test de profundidad
	glShadeModel(GL_SMOOTH);
}

/**
 * Posible cambio del tamaño de la ventana
 */
void reshape(int width, int height) {

	glViewport(0, 0, width, height);
	w = width;
	h = height;
	if (w > h)
		Z = w;
	else
		Z = h;
	R2 = (w * w + h * h + Z * Z) / 4;
	inicializaVista();
}

/**
 * Crea la ventana
 */
void inicializaVentana(int argc, char **argv) {
	glutInit(&argc, argv);
//  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); // Activar Z-Buffer
	glutInitWindowSize(700, 700);
	glutInitWindowPosition(200, 30);
	glutCreateWindow(argv[0]);
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
}

void iniciaRaton(void) {
	glutMouseFunc(botonRatonPulsado); // Eventos de pulsacion de botones de raton
	glutMotionFunc(ratonMovido);
}

void iniciaDisplayLists(void) {
	escenario = glGenLists(1);
	if (escenario != 0) {
		glNewList(escenario, GL_COMPILE);
		escena();
		glEndList();
	}
}

/**
 * Distribuye el juego
 */
void pintaModelo(int argc, char **argv) {
	inicializaVentana(argc, argv);
	inicializaTeclado();
	iniciaRaton();
	inicializaVista();
	iniciaLuces();
	inicializaTexturas();
	iniciaDisplayLists();
	glutMainLoop();
}

int main(int argc, char** argv) {
	pintaModelo(argc, argv);
	return (1);
}
